package controllers

import java.io.File
import dao._
import implicits.Implicits._

import javax.inject.Inject
import models.Tables._
import org.joda.time.DateTime
import play.api.i18n.I18nSupport
import play.api.mvc._
import shared.Pojo._
import shared.VarTool
import tool.{FileTool, FormTool, MatrixFileTool, Tool}
import utils.Utils

import scala.collection.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import upickle.default._

/**
 * Created by yz on 23/7/2020
 */
class MatrixFileDataController @Inject()(cc: ControllerComponents)(
  implicit val modeDao: ModeDao,
  implicit val matrixFileDataDao: MatrixFileDataDao,
  implicit val missionDao: MissionDao,
  implicit val groupFileDataDao: GroupFileDataDao
) extends AbstractController(cc) with MatrixFileTool with I18nSupport {

  import shared.pojo.Pojo.matrixFileData._

  def getAll = Action.async { implicit request =>
    val userId = Tool.getUserId
    val f = matrixFileDataDao.selectAll(userId)
    f.map { x =>
      Future {
        val fileNames = x.map(x => s"${x.fileName}")
        val kindDir = getUserDataKindDir(userId)
        kindDir.listFiles().filter {
          file =>
            !fileNames.contains(file.getName)
        }.foreach(_.deleteQuietly)
      }
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }

  def all = Action.async { implicit request =>
    val data=Tool.transformRq.transform(reader[GetAll.Request])
    val f = matrixFileDataDao.selectAll(data.id)
    f.map { x =>
      Future {
        val fileNames = x.map(x => s"${x.fileName}")
        val kindDir = getUserDataKindDir(data.id)
        kindDir.listFiles().filter {
          file =>
            !fileNames.contains(file.getName)
        }.foreach(_.deleteQuietly)
      }
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }

  def add = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.metDataAddForm.bindFromRequest().get
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val userId = Tool.getUserId
    val tmpFile = Tool.getInitFile(data, tmpDir, MetDataKind.defaultFileName)
    val file = data.varUniq.map { x =>
      Tool.varUniqDeal(tmpFile)
    }.getOrElse(tmpFile)
    val myMessage = FileTool.fileCheck(file).toMyMessage
    if (!myMessage.valid) {
      Tool.deleteDirectory(tmpDir)
      Future.successful(Ok(ujson.Obj("message" -> myMessage.message, "valid" -> false)))
    } else {
      matrixFileDataDao.selectAllFileName(userId).flatMap { fileNames =>
        val filePrefix = file.namePrefix
        val newFileName = Tool.getNewFileName(fileNames, file)
        val kindDir = getUserDataKindDir(userId)
        val newFile = new File(kindDir, s"${newFileName}")
        val lines = file.txtLines
        val newLines = Tool.na2zero(lines)
        newLines.toTxtFile(newFile)
        val row = MatrixFileDataRow(id = 0, fileName = s"${newFile.getName}", userId = userId, tags = data.tags,
          comment = data.comment, uploadTime = new DateTime(), size = file.size.toInt, pId = 0)
        matrixFileDataDao.insert(row)
      }.map { x =>
        Tool.deleteDirectory(tmpDir)
        Ok(ujson.Obj("valid" -> true))
      }
    }
  }

  def getHeaders = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      matrixFileDataDao.select(data.id).map {
        row =>
          val file = getUserDataKindFile(row.userId, row.fileName)
          val lines = file.txtLines
          val columnNames = lines.head.drop(1)
          val rowNames = lines.map(_.head).drop(1)
          Ok(ujson.Obj("headers" -> columnNames, "fileName" -> file.getName, "rows" -> rowNames))
      }
  }

  def getNewFileName = Action.async { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    val kind = MetDataKind.name
    val userId = Tool.getUserId
    matrixFileDataDao.select(data.id).zip(matrixFileDataDao.selectAllFileName(userId)).map { case (row, fileNames) =>
      val dataFile = getUserDataKindFile(row.userId, row.fileName)
      val fileName = s"${dataFile.namePrefix}_sub.txt"
      val filePrefix = fileName.fileNamePrefix
      val newFileName = Tool.getNewFileName(fileNames, fileName)
      Ok(ujson.Obj("fileName" -> newFileName))
    }
  }

  def sub = Action.async { implicit request =>
    val data = FormTool.subForm.bindFromRequest().get
    val userId = Tool.getUserId
    matrixFileDataDao.select(data.id).flatMap { fileDataRow =>
      val dataFile = getUserDataKindFile(fileDataRow.userId, fileDataRow.fileName)
      val dbLines = dataFile.txtLines
      val dbHeaders = dbLines.head
      val kindDir = getUserDataKindDir(userId)
      val newFile = new File(kindDir, s"${data.fileName}.txt")
      val headers = dbHeaders.head :: data.cols
      val rows = dbHeaders.head :: data.rows
      val lines = dataFile.txtLines.selectColumns(headers).filter { columns =>
        rows.contains(columns.head)
      }
      lines.toTxtFile(newFile)
      val pId = if (fileDataRow.pId != 0) {
        fileDataRow.pId
      } else data.id
      val row = MatrixFileDataRow(id = 0, fileName = s"${newFile.getName}", userId = userId, tags = fileDataRow.tags,
        comment = fileDataRow.comment, uploadTime = new DateTime(), size = newFile.size.toInt, pId = pId)
      matrixFileDataDao.insert(row).map { x =>
        Ok(ujson.Obj("valid" -> true))
      }
    }
  }

  def deleteWithSub = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idsForm.bindFromRequest().get
      matrixFileDataDao.selectAll(data.ids).zip(matrixFileDataDao.selectAllByPids(userId, data.ids)).flatMap { case (pRows, subRows) =>
        val allRows = pRows ::: subRows.toList
        Future {
          allRows.foreach { row =>
            val file = getUserDataKindFile(row.userId, row.fileName)
            file.deleteQuietly
          }
        }
        val ids = allRows.map(_.id)
        matrixFileDataDao.deletes(userId, ids).map { x =>
          Ok(writeJs("success"))
        }
      }
  }

  def existSub = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idsForm.bindFromRequest().get
      matrixFileDataDao.selectAllByPids(userId, data.ids).map { xs =>
        val subIds = xs.map(_.id).toList
        val exist = !data.ids.containsAll(subIds)
        Ok(ujson.Obj("exist" -> exist))
      }
  }

  def deletes = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idsForm.bindFromRequest().get
      matrixFileDataDao.selectAll(data.ids).flatMap { rows =>
        Future {
          rows.foreach { row =>
            val file = getUserDataKindFile(row.userId, row.fileName)
            file.deleteQuietly
          }
        }
        matrixFileDataDao.deletes(userId, data.ids).flatMap { x =>
          matrixFileDataDao.update2RootByPids(data.ids)
        }.map { x =>
          Ok(writeJs("success"))
        }
      }
  }

  def update = Action.async { implicit request =>
    val id = FormTool.idForm.bindFromRequest().get.id
    val data = FormTool.updateForm.bindFromRequest().get
    matrixFileDataDao.select(id).flatMap { row =>
      val newRow = row.copy(fileName = data.fileName.addSuffix, tags = data.tags, comment = data.comment)
      val kindDir = getUserDataKindDir(row.userId)
      val newFile = new File(kindDir, s"${data.fileName.addSuffix}")
      new File(kindDir, row.fileName).renameTo(newFile)
      matrixFileDataDao.update(newRow).map { x =>
        Ok(writeJs("success"))
      }
    }
  }

  def importRun = Action.async { implicit request =>
    val originalFileName = FormTool.originalFileNameForm.bindFromRequest().get.originalFileName
    val data = FormTool.updateForm.bindFromRequest().get
    val userId = Tool.getUserId
    Tool.produceRawDataLinesF(userId, originalFileName).flatMap { lines =>
      val fileName=data.fileName.addSuffix
      val kindDir = getUserDataKindDir(userId)
      val newFile = new File(kindDir, s"${fileName}")
      lines.toTxtFile(newFile)
      val row = MatrixFileDataRow(id = 0, fileName = s"${fileName}", userId = userId, tags = data.tags,
        comment = data.comment, uploadTime = new DateTime(), size = newFile.size.toInt, pId = 0)
      matrixFileDataDao.insert(row).map { x =>
        Ok(writeJs("success"))
      }
    }
  }

  def getFileData = Action.async { implicit request =>
    val softKindOp = FormTool.softKindOpForm.bindFromRequest().get.softKind
    val fileF = softKindOp.map { softKind =>
      Future {
        val fileName = FormTool.fileNameForm.bindFromRequest().get.fileName
        val dir = new File(Tool.softExampleDir, softKind)
        new File(dir, fileName)
      }
    }.getOrElse {
      val data = FormTool.idForm.bindFromRequest().get
      matrixFileDataDao.select(data.id).map { row =>
        getUserDataKindFile(row.userId, row.fileName)
      }
    }
    fileF.map { file =>
      val (columnNames, array) = Utils.getInfoByLines(file.txtLines)
      val json = ujson.Obj("columnNames" -> columnNames, "array" -> array)
      Ok(json)
    }
  }

  def getFileDataById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      matrixFileDataDao.select(data.id).map {
        x =>
          val json = writeJs(x).toObj ++ ujson.Obj("kind" -> MetDataKind.name)
          Ok(json)
      }
  }

  def downloadFileById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      matrixFileDataDao.select(data.id).map {
        row =>
          val file = getUserDataKindFile(row.userId, row.fileName)
          Ok.sendFile(file).withHeaders(
            CONTENT_DISPOSITION -> Tool.getContentDisposition(s"${
              file.getName
            }"),
            CONTENT_TYPE -> "application/x-download"
          )
      }
  }

  def detailBefore = Action.async {
    implicit request =>
      val idData = FormTool.idOpForm.bindFromRequest().get
      val userId = Tool.getUserId
      idData.id.map {
        x =>
          Future {
            Ok(views.html.user.data.matrixDetail(x))
          }
      }.getOrElse {
        val data = FormTool.fileNameForm.bindFromRequest().get
        val fileName = data.fileName
        val names = fileName.split("/")
        val kind = VarTool.showDirNameMap(names(0)).name
        matrixFileDataDao.select(userId, names(1)).map(_.id).map {
          x =>
            Ok(views.html.user.data.matrixDetail(x))
        }
      }
  }

  def fileNameNonExistCheck = Action.async {
    implicit request =>
      val data = FormTool.fileNameForm.bindFromRequest.get
      val kind = FormTool.kindForm.bindFromRequest.get.kind
      val originalFileNameOp = FormTool.originalFileNameOpForm.bindFromRequest().get.originalFileName
      val userId = Tool.getUserId
      val fileName = data.fileName.addSuffix
      matrixFileDataDao.selectAllFileName(userId).map { fileNames =>
        val valid = originalFileNameOp.map { originalFileName =>
          if (originalFileName == data.fileName) {
            true
          } else {
            !fileNames.contains(fileName)
          }
        }.getOrElse {
          !fileNames.contains(fileName)
        }
        Ok(ujson.Obj("valid" -> valid))
      }
  }


}
